<template>
  <div class="virtual-table">
    <div class="virtual-table__header">
      <div class="virtual-table__row">
        <div v-for="column in columns" :key="column.key" class="virtual-table__cell virtual-table__header-cell" :style="{ width: column.width }">
          {{ column.title }}
        </div>
      </div>
    </div>

    <!-- 配置区域 -->
    <div class="config-panel">
      <div class="config-item">
        <label>父项数量：</label>
        <input type="number" v-model.number="configOptions.parentCount" @change="regenerateData" min="1" max="100000" />
      </div>
      <div class="config-item">
        <label>子项数量模式：</label>
        <select v-model="configOptions.childMode" @change="handleChildModeChange">
          <option value="random">随机</option>
          <option value="fixed">固定</option>
          <option value="incremental">递增</option>
          <option value="exponential">指数增长</option>
        </select>
      </div>
      <div class="config-item" v-if="configOptions.childMode === 'random' || configOptions.childMode === 'incremental'">
        <label>子项数量范围：</label>
        <input type="number" v-model.number="configOptions.minChildCount" @change="regenerateData" min="1" max="1000" />
        -
        <input type="number" v-model.number="configOptions.maxChildCount" @change="regenerateData" min="1" max="100000" />
      </div>
      <div class="config-item" v-if="configOptions.childMode === 'fixed'">
        <label>每个父项的子项数量：</label>
        <input type="number" v-model.number="configOptions.fixedChildCount" @change="regenerateData" min="1" max="100000" />
      </div>
      <div class="config-item" v-if="configOptions.childMode === 'exponential'">
        <label>基础数量：</label>
        <input type="number" v-model.number="configOptions.expBaseCount" @change="regenerateData" min="1" max="100" />
        <span>（2^n × 基础数量）</span>
      </div>
      <div class="config-item">
        <label>优化生成:</label>
        <input type="checkbox" v-model="configOptions.useWorker" />
        <span>使用分批异步生成(大数据量推荐)</span>
      </div>
    </div>

    <!-- 性能测试面板 -->
    <div class="test-panel">
      <div class="test-item">
        <button @click="runPerformanceTest" :disabled="isTestRunning">运行性能测试</button>
        <div class="test-status" v-if="isTestRunning">测试中，请稍候...</div>
      </div>
      <div class="test-item">
        <label>缓冲区大小：</label>
        <input type="number" v-model.number="configOptions.bufferSize" min="1" max="100" />
      </div>
      <div class="test-item">
        <label>子列表高度：</label>
        <input type="number" v-model.number="configOptions.subListHeight" @input="updateSubListHeight" min="50" max="800" />
        px
      </div>
      <div class="loading-indicator" v-if="isGeneratingData">
        <div class="spinner"></div>
        <div class="loading-text">正在生成数据...{{ generatingProgress }}%</div>
      </div>

      <!-- 性能测试结果 -->
      <div class="test-results" v-if="testResults.length > 0">
        <h3>性能测试结果</h3>
        <table>
          <thead>
            <tr>
              <th>测试项</th>
              <th>时间(ms)</th>
              <th>描述</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(result, index) in testResults" :key="index">
              <td>{{ result.name }}</td>
              <td>{{ result.time.toFixed(2) }}</td>
              <td>{{ result.description }}</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>

    <div class="virtual-table__stats">
      <div>总父项数: {{ tableData.length }}</div>
      <div>可见父项数: {{ visibleData.length }}</div>
      <div>已展开行数: {{ expandedRows.length }}</div>
      <div>当前展开行子项总数: {{ getTotalExpandedChildrenCount() }}</div>
    </div>

    <div class="virtual-table__body" ref="virtualBody" @scroll="handleScroll">
      <div class="virtual-table__phantom" :style="{ height: totalHeight + 'px' }"></div>
      <div class="virtual-table__visible-items" :style="{ transform: `translateY(${startOffset}px)` }">
        <template v-for="(item, index) in visibleData" :key="item.id">
          <!-- 父行 -->
          <div class="virtual-table__row" :class="{ 'virtual-table__row--expanded': expandedRows.includes(item.id) }" @click="toggleRow(item.id)">
            <div v-for="column in columns" :key="column.key" class="virtual-table__cell" :style="{ width: column.width }">
              <template v-if="column.key === 'expand'">
                <span class="expand-icon">{{ expandedRows.includes(item.id) ? '▼' : '▶' }}</span>
              </template>
              <template v-else-if="column.key === 'childrenCount'">
                {{ item.children.length }}
              </template>
              <template v-else>
                {{ item[column.key] }}
              </template>
            </div>
          </div>

          <!-- 子行容器 -->
          <div v-if="expandedRows.includes(item.id)" class="virtual-table__sub-container">
            <!-- 子列表滚动容器 -->
            <div class="virtual-table__sub-scroll-container" :style="{ height: configOptions.subListHeight + 'px' }" @scroll="e => handleChildScroll(item.id, e)">
              <div class="virtual-table__sub-phantom" :style="{ height: getChildrenTotalHeight(item.id) + 'px' }"></div>
              <div class="virtual-table__sub-visible-items" :style="{ transform: `translateY(${getChildrenOffset(item.id)}px)` }">
                <div v-for="child in getVisibleChildren(item.id)" :key="child.id" class="virtual-table__row virtual-table__row--child">
                  <div v-for="column in childColumns" :key="column.key" class="virtual-table__cell" :style="{ width: column.width }">
                    {{ child[column.key] }}
                  </div>
                </div>
              </div>
            </div>
          </div>
        </template>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, nextTick, watch } from 'vue';

// 定义表格列
const columns = [
  { key: 'expand', title: '', width: '50px' },
  { key: 'name', title: '名称', width: '150px' },
  { key: 'childrenCount', title: '子项数量', width: '100px' },
  { key: 'age', title: '年龄', width: '80px' },
  { key: 'address', title: '地址', width: '220px' },
];

// 子表格列
const childColumns = [
  { key: 'name', title: '名称', width: '200px' },
  { key: 'value', title: '数值', width: '100px' },
  { key: 'date', title: '日期', width: '150px' },
];

// 虚拟滚动相关参数
const ITEM_HEIGHT = 50; // 每行高度
const CHILD_ITEM_HEIGHT = 40; // 子项高度

// 性能测试状态和结果
const isTestRunning = ref(false);
const testResults = ref([]);
const isGeneratingData = ref(false);
const generatingProgress = ref(0);

// 可配置选项
const configOptions = ref({
  parentCount: 1000, // 父项数量
  childMode: 'random', // 子项数量模式：random, fixed, incremental, exponential
  minChildCount: 10, // 最小子项数量
  maxChildCount: 200, // 最大子项数量
  fixedChildCount: 1000, // 固定子项数量
  expBaseCount: 10, // 指数增长基础数量
  bufferSize: 20, // 缓冲区大小
  childVisibleCount: 20, // 子列表默认可见数量
  subListHeight: 200, // 子列表高度(px)
  useWorker: true, // 使用分批异步生成
});

// 引用和状态
const virtualBody = ref(null);
const scrollTop = ref(0);
const expandedRows = ref([]); // 存储展开的行ID
const childScrollStates = ref({}); // 每个父项的子列表滚动状态

// 模拟数据
const tableData = ref([]);

// 子项数量模式变更处理
const handleChildModeChange = () => {
  regenerateData();
};

// 获取已展开行的子项总数
const getTotalExpandedChildrenCount = () => {
  let count = 0;
  expandedRows.value.forEach(id => {
    const item = tableData.value.find(item => item.id === id);
    if (item) {
      count += item.children.length;
    }
  });
  return count;
};

// 重新生成数据
const regenerateData = () => {
  // 清空展开的行
  expandedRows.value = [];
  // 清空子列表滚动状态
  childScrollStates.value = {};
  // 清空测试结果
  testResults.value = [];

  if (configOptions.value.useWorker && configOptions.value.parentCount > 1000) {
    // 使用分批异步生成方式
    generateDataAsync(configOptions.value.parentCount).then(data => {
      tableData.value = data;

      // 如果已经挂载了，重新计算滚动区域
      if (virtualBody.value) {
        nextTick(() => {
          virtualBody.value.scrollTop = 0;
          scrollTop.value = 0;
          virtualBody.value.dispatchEvent(new Event('scroll'));
        });
      }
    });
  } else {
    // 普通方式同步生成
    tableData.value = generateData(configOptions.value.parentCount);

    // 如果已经挂载了，重新计算滚动区域
    if (virtualBody.value) {
      nextTick(() => {
        virtualBody.value.scrollTop = 0;
        scrollTop.value = 0;
        virtualBody.value.dispatchEvent(new Event('scroll'));
      });
    }
  }
};

// 异步生成数据
const generateDataAsync = async count => {
  isGeneratingData.value = true;
  generatingProgress.value = 0;

  try {
    console.time('异步生成数据');

    // 将父项数据生成分批进行
    const batchSize = 100; // 每批生成的父项数量
    const batches = Math.ceil(count / batchSize);
    const result = [];

    for (let batchIndex = 0; batchIndex < batches; batchIndex++) {
      // 使用setTimeout创建宏任务，让UI有机会更新
      await new Promise(resolve => {
        setTimeout(() => {
          const startIndex = batchIndex * batchSize;
          const endIndex = Math.min(startIndex + batchSize, count);

          // 生成这一批的数据
          for (let i = startIndex; i < endIndex; i++) {
            // 根据不同的子项模式生成子项数量
            let childCount = calculateChildCount(i, count);

            // 限制最大子项数量，避免浏览器崩溃
            childCount = Math.min(childCount, 100000);

            result.push({
              id: i,
              name: `父项 ${i}`,
              age: Math.floor(Math.random() * 50) + 20,
              address: `地址 ${i}, 某某市某某区`,
              children: generateChildrenData(i, childCount),
            });
          }

          // 更新进度
          generatingProgress.value = Math.floor((endIndex / count) * 100);
          resolve();
        }, 0);
      });
    }

    console.timeEnd('异步生成数据');
    return result;
  } finally {
    isGeneratingData.value = false;
  }
};

// 根据模式计算子项数量
const calculateChildCount = (index, totalCount) => {
  let childCount = 0;

  switch (configOptions.value.childMode) {
    case 'random':
      // 随机模式: 子项数量在最小值和最大值之间随机
      childCount = Math.floor(Math.random() * (configOptions.value.maxChildCount - configOptions.value.minChildCount + 1) + configOptions.value.minChildCount);
      break;

    case 'fixed':
      // 固定模式: 每个父项有相同数量的子项
      childCount = configOptions.value.fixedChildCount;
      break;

    case 'incremental':
      // 递增模式: 子项数量随父项索引递增
      const step = Math.floor((configOptions.value.maxChildCount - configOptions.value.minChildCount) / Math.max(1, totalCount - 1));
      childCount = configOptions.value.minChildCount + index * step;
      break;

    case 'exponential':
      // 指数增长模式: 子项数量指数增长 (2^n * 基础数量)
      // 将索引映射到0-10的范围内，避免数量太大
      const power = Math.min(10, Math.floor(index / (totalCount / 10)));
      childCount = Math.pow(2, power) * configOptions.value.expBaseCount;
      break;
  }

  return childCount;
};

// 模拟大量数据 (实际项目中可能从API获取)
const generateData = count => {
  console.time('生成数据');
  const data = [];
  for (let i = 0; i < count; i++) {
    // 根据不同的子项模式生成子项数量
    let childCount = calculateChildCount(i, count);

    // 限制最大子项数量，避免浏览器崩溃
    childCount = Math.min(childCount, 100000);

    data.push({
      id: i,
      name: `父项 ${i}`,
      age: Math.floor(Math.random() * 50) + 20,
      address: `地址 ${i}, 某某市某某区`,
      children: generateChildrenData(i, childCount),
    });
  }
  console.timeEnd('生成数据');
  return data;
};

// 生成子项数据(使用记忆化优化复用相同数量的子项数据)
const childrenCache = new Map();
const generateChildrenData = (parentId, count) => {
  // 对于固定模式，如果数量相同且较大，可以复用相同数量的子项结构，只修改ID和名称
  if (configOptions.value.childMode === 'fixed' && count > 1000) {
    const cacheKey = `count-${count}`;
    if (!childrenCache.has(cacheKey)) {
      // 缓存中没有，创建新的
      childrenCache.set(cacheKey, createChildItems(0, count));
    }

    // 复制已有结构，仅修改ID和名称
    const baseChildren = childrenCache.get(cacheKey);
    return baseChildren.map((child, index) => ({
      ...child,
      id: `${parentId}-${index}`,
      name: `子项 ${parentId}-${index}`,
    }));
  }

  // 其他情况，创建新的子项
  return createChildItems(parentId, count);
};

// 创建子项数据的核心函数
const createChildItems = (parentId, count) => {
  const children = [];

  // 批量生成，避免循环次数过多导致性能问题
  const batchSize = 1000;
  for (let i = 0; i < count; i += batchSize) {
    const currentBatch = Math.min(batchSize, count - i);
    for (let j = 0; j < currentBatch; j++) {
      const index = i + j;
      children.push({
        id: `${parentId}-${index}`,
        name: `子项 ${parentId}-${index}`,
        value: Math.floor(Math.random() * 1000),
        date: new Date(Date.now() - Math.floor(Math.random() * 10000000000)).toLocaleDateString(),
      });
    }
  }

  return children;
};

// 运行性能测试
const runPerformanceTest = async () => {
  if (isTestRunning.value) return;

  isTestRunning.value = true;
  testResults.value = [];

  try {
    // 测试数据生成性能
    const t1 = performance.now();

    if (configOptions.value.useWorker && configOptions.value.parentCount > 1000) {
      tableData.value = await generateDataAsync(configOptions.value.parentCount);
    } else {
      tableData.value = generateData(configOptions.value.parentCount);
    }

    const t2 = performance.now();
    testResults.value.push({
      name: '数据生成',
      time: t2 - t1,
      description: `生成${configOptions.value.parentCount}个父项数据` + (configOptions.value.useWorker ? ' (分批异步)' : ' (同步)'),
    });

    // 等待DOM更新
    await nextTick();

    // 测试初始渲染性能
    const t3 = performance.now();
    if (virtualBody.value) {
      virtualBody.value.dispatchEvent(new Event('scroll'));
    }
    const t4 = performance.now();
    testResults.value.push({
      name: '初始渲染',
      time: t4 - t3,
      description: `渲染可见区域的父项`,
    });

    // 测试展开第一个父项的性能
    const t5 = performance.now();
    const firstItemId = tableData.value[0]?.id;
    if (firstItemId !== undefined) {
      expandedRows.value.push(firstItemId);
      await nextTick();
    }
    const t6 = performance.now();

    const firstItemChildCount = tableData.value[0]?.children.length || 0;
    testResults.value.push({
      name: '展开父项',
      time: t6 - t5,
      description: `展开第一个父项（包含${firstItemChildCount}个子项）`,
    });

    // 滚动测试
    const t7 = performance.now();
    if (virtualBody.value) {
      // 模拟滚动到中间位置
      virtualBody.value.scrollTop = virtualBody.value.scrollHeight / 2;
      await new Promise(resolve => setTimeout(resolve, 100));
    }
    const t8 = performance.now();
    testResults.value.push({
      name: '滚动测试',
      time: t8 - t7,
      description: `滚动到中间位置并重新渲染`,
    });

    // 如果展开了第一项，则测试子列表滚动
    if (expandedRows.value.includes(firstItemId) && firstItemChildCount > 100) {
      const t9 = performance.now();
      // 模拟子列表滚动
      await new Promise(resolve => {
        childScrollStates.value[firstItemId] = {
          scrollTop: CHILD_ITEM_HEIGHT * 50, // 滚动到第50项
          visibleCount: Math.ceil(configOptions.value.subListHeight / CHILD_ITEM_HEIGHT),
        };
        setTimeout(resolve, 100);
      });
      const t10 = performance.now();
      testResults.value.push({
        name: '子列表滚动',
        time: t10 - t9,
        description: `子列表滚动到第50项的位置`,
      });
    }
  } finally {
    isTestRunning.value = false;
  }
};

// 更新子列表高度
const updateSubListHeight = () => {
  // 更新后，如果有展开的行，需要重新计算
  if (expandedRows.value.length > 0) {
    // 更新所有展开的子列表的可见数量
    nextTick(() => {
      expandedRows.value.forEach(id => {
        if (childScrollStates.value[id]) {
          const visibleCount = Math.ceil(configOptions.value.subListHeight / CHILD_ITEM_HEIGHT);
          childScrollStates.value[id].visibleCount = visibleCount;
        }
      });
    });
  }
};

// 计算总高度
const totalHeight = computed(() => {
  return tableData.value.length * ITEM_HEIGHT;
});

// 计算可见区域的起始索引和结束索引
const visibleRange = computed(() => {
  if (!virtualBody.value) return { start: 0, end: 10 };

  const visibleHeight = virtualBody.value.clientHeight;
  const start = Math.floor(scrollTop.value / ITEM_HEIGHT);
  const visibleCount = Math.ceil(visibleHeight / ITEM_HEIGHT);
  const bufferedStart = Math.max(0, start - configOptions.value.bufferSize);
  const bufferedEnd = Math.min(tableData.value.length, start + visibleCount + configOptions.value.bufferSize);

  return { start: bufferedStart, end: bufferedEnd };
});

// 计算可见数据
const visibleData = computed(() => {
  const { start, end } = visibleRange.value;
  return tableData.value.slice(start, end);
});

// 计算起始偏移量
const startOffset = computed(() => {
  const { start } = visibleRange.value;
  return start * ITEM_HEIGHT;
});

// 处理滚动事件
const handleScroll = e => {
  if (virtualBody.value) {
    scrollTop.value = virtualBody.value.scrollTop;
  }
};

// 切换行的展开/收起状态
const toggleRow = id => {
  const index = expandedRows.value.indexOf(id);
  if (index === -1) {
    expandedRows.value.push(id);
    // 初始化子列表滚动状态
    const visibleCount = Math.ceil(configOptions.value.subListHeight / CHILD_ITEM_HEIGHT);
    childScrollStates.value[id] = { scrollTop: 0, visibleCount: visibleCount };
  } else {
    expandedRows.value.splice(index, 1);
  }
};

// 获取指定父项的子项总高度
const getChildrenTotalHeight = parentId => {
  const parent = tableData.value.find(item => item.id === parentId);
  if (!parent) return 0;
  return parent.children.length * CHILD_ITEM_HEIGHT;
};

// 获取子项的偏移量
const getChildrenOffset = parentId => {
  const state = childScrollStates.value[parentId];
  if (!state) return 0;
  return state.scrollTop;
};

// 获取可见的子项
const getVisibleChildren = parentId => {
  const parent = tableData.value.find(item => item.id === parentId);
  if (!parent) return [];

  const defaultVisibleCount = Math.ceil(configOptions.value.subListHeight / CHILD_ITEM_HEIGHT);
  const state = childScrollStates.value[parentId] || { scrollTop: 0, visibleCount: defaultVisibleCount };
  const start = Math.floor(state.scrollTop / CHILD_ITEM_HEIGHT);
  const visibleItemCount = state.visibleCount;

  // 添加缓冲区
  const bufferedStart = Math.max(0, start - configOptions.value.bufferSize);
  const bufferedEnd = Math.min(parent.children.length, start + visibleItemCount + configOptions.value.bufferSize);

  return parent.children.slice(bufferedStart, bufferedEnd);
};

// 处理子列表的滚动
const handleChildScroll = (parentId, e) => {
  if (!childScrollStates.value[parentId]) return;

  const target = e.target;
  if (target) {
    childScrollStates.value[parentId].scrollTop = target.scrollTop;

    // 根据容器高度计算可见数量
    const visibleCount = Math.ceil(target.clientHeight / CHILD_ITEM_HEIGHT);
    childScrollStates.value[parentId].visibleCount = visibleCount;
  }
};

// 组件挂载后初始化
onMounted(() => {
  // 初始生成数据
  if (configOptions.value.useWorker && configOptions.value.parentCount > 1000) {
    generateDataAsync(configOptions.value.parentCount).then(data => {
      tableData.value = data;
      updateVisibleRange();
    });
  } else {
    tableData.value = generateData(configOptions.value.parentCount);
    updateVisibleRange();
  }
});

// 监听展开行变化，重新计算布局
watch(expandedRows, () => {
  nextTick(() => {
    if (virtualBody.value) {
      // 强制重新计算滚动区域
      virtualBody.value.dispatchEvent(new Event('scroll'));
    }
  });
});

// 监听缓冲区大小变化
watch(
  () => configOptions.value.bufferSize,
  () => {
    if (virtualBody.value) {
      virtualBody.value.dispatchEvent(new Event('scroll'));
    }
  }
);

// 更新可视范围
const updateVisibleRange = () => {
  if (virtualBody.value) {
    const { clientHeight } = virtualBody.value;
    const visibleCount = Math.ceil(clientHeight / ITEM_HEIGHT);

    // 更新初始可见项数量
    const range = visibleRange.value;
    range.end = Math.min(tableData.value.length, visibleCount + configOptions.value.bufferSize);
  }
};
</script>

<style scoped>
.virtual-table {
  width: 100%;
  height: 700px;
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  overflow: hidden;
  position: relative;
}

.virtual-table__header {
  background-color: #fafafa;
  border-bottom: 1px solid #e8e8e8;
  overflow: hidden;
  width: 100%;
}

/* 配置面板样式 */
.config-panel {
  padding: 12px;
  background-color: #f9f9f9;
  border-bottom: 1px solid #e8e8e8;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

.config-item {
  margin-right: 24px;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
}

.config-item label {
  margin-right: 8px;
  font-size: 14px;
  color: #333;
}

.config-item input,
.config-item select {
  width: 120px;
  padding: 4px 8px;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
}

.config-item span {
  margin-left: 5px;
  font-size: 12px;
  color: #666;
}

/* 测试面板样式 */
.test-panel {
  padding: 12px;
  background-color: #f0f8ff;
  border-bottom: 1px solid #e8e8e8;
}

.test-item {
  margin-right: 24px;
  margin-bottom: 8px;
  display: inline-flex;
  align-items: center;
}

.test-item button {
  padding: 6px 16px;
  background-color: #1890ff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.test-item button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

.test-status {
  margin-left: 10px;
  color: #ff4d4f;
}

.test-results {
  margin-top: 12px;
  padding: 12px;
  border-top: 1px solid #e8e8e8;
}

.test-results h3 {
  margin-top: 0;
  margin-bottom: 10px;
  font-size: 16px;
}

.test-results table {
  width: 100%;
  border-collapse: collapse;
}

.test-results th,
.test-results td {
  padding: 8px;
  border: 1px solid #e8e8e8;
  text-align: left;
}

.test-results th {
  background-color: #fafafa;
}

/* 统计信息 */
.virtual-table__stats {
  padding: 8px 12px;
  display: flex;
  background-color: #e6f7ff;
  border-bottom: 1px solid #e8e8e8;
  font-size: 14px;
}

.virtual-table__stats div {
  margin-right: 20px;
}

.virtual-table__body {
  position: relative;
  overflow-y: auto;
  height: calc(100% - 220px); /* 减去表头和配置面板高度 */
}

.virtual-table__phantom {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: -1;
}

.virtual-table__visible-items {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
}

.virtual-table__row {
  display: flex;
  border-bottom: 1px solid #e8e8e8;
  cursor: pointer;
  transition: background-color 0.3s;
  height: 50px;
  line-height: 50px;
}

.virtual-table__row:hover {
  background-color: #f5f5f5;
}

.virtual-table__row--expanded {
  background-color: rgba(230, 247, 255, 0.7);
}

.virtual-table__row--child {
  height: 40px;
  line-height: 40px;
  background-color: #fafafa;
  padding-left: 20px;
}

.virtual-table__cell {
  padding: 0 16px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.virtual-table__header-cell {
  font-weight: bold;
  height: 50px;
  line-height: 50px;
}

.expand-icon {
  display: inline-block;
  width: 16px;
  text-align: center;
  transition: transform 0.3s;
}

.virtual-table__sub-container {
  position: relative;
  width: 100%;
  overflow: hidden;
}

.virtual-table__sub-scroll-container {
  position: relative;
  width: 100%;
  height: 200px; /* 子列表高度，由配置控制 */
  overflow-y: auto;
}

.virtual-table__sub-phantom {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: -1;
}

.virtual-table__sub-visible-items {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
}

/* 加载指示器 */
.loading-indicator {
  display: flex;
  align-items: center;
  margin-top: 10px;
  padding: 10px;
  background-color: #fffbe6;
  border: 1px solid #ffe58f;
  border-radius: 4px;
}

.spinner {
  width: 20px;
  height: 20px;
  border: 2px solid #1890ff;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  margin-right: 8px;
}

.loading-text {
  color: #555;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
</style>
